<?php


namespace Bxy\Jwt;

use Bxy\Jwt\annotation\AuthAnnotation;
use Bxy\Jwt\helper\YiiSoft\Arrays\ArrayHelper;
use Hyperf\Di\Annotation\AnnotationCollector;
use Hyperf\Utils\ApplicationContext;
use Hyperf\Utils\Context;
use Psr\Http\Message\ServerRequestInterface;
use Hyperf\HttpServer\Router\Dispatched;
use Hyperf\Contract\ConfigInterface;
trait TraitBxyAuth
{
    /**
     * @return array|null
     * User : 码农先生
     * Email: 807098383@qq.com
     * Website: http://blog.zsboss888.com
     * 获取配置暂未实现
     */
    public function getAuthConfig():?array
    {

        return  ApplicationContext::getContainer()->get(ConfigInterface::class)->get('auth');
    }
    /**
     * 检测注解路由
     *
     * @param ServerRequestInterface $request
     * @param Dispatched             $dispatched
     *
     * @return array
     */
    public function checkRouter(ServerRequestInterface $request, Dispatched $dispatched): array
    {
        /**
         * @var AuthAnnotation $authAnnotation
         */
        $authAnnotation = self::getAnnotation(AuthAnnotation::class, $dispatched);
        if ($authAnnotation !== null) {
            $isPublic = $authAnnotation->isPublic;
            $isWhitelist = $authAnnotation->isWhitelist;
            $isCheck   = $authAnnotation->isCheck;
        } else {
            $path = $request->getUri()->getPath();
            $isPublic = $this->checkPublicList($path);
            $isWhitelist = $this->checkWhitelist($path);
            $isCheck = false;
        }
        return [$isPublic, $isWhitelist,$isCheck];
    }

    public static function getAnnotation(string $annotation, Dispatched $dispatched = null)
    {
        //
        if($dispatched===null){
            $request = Context::get(ServerRequestInterface::class);
            $dispatched = $request->getAttribute(Dispatched::class);
        }
        [$class, $method] = $dispatched->handler->callback;
        if($class){
            $classMethodAnnotations = AnnotationCollector::getClassMethodAnnotation($class,$method);
            return self::getValue($classMethodAnnotations,$annotation);
        }

    }
    public static function getValue($array, $key, $default = null)
    {
        if ($key instanceof \Closure) {
            return $key($array, $default);
        }

        if (is_array($key)) {
            $lastKey = array_pop($key);
            foreach ($key as $keyPart) {
                $array = static::getValue($array, $keyPart);
            }
            $key = $lastKey;
        }

        if (is_array($array) && (isset($array[$key]) || array_key_exists($key, $array))) {
            return $array[$key];
        }

        if (($pos = strrpos($key, '.')) !== false) {
            $array = static::getValue($array, substr($key, 0, $pos), $default);
            $key = substr($key, $pos + 1);
        }

        if (is_object($array)) {
            // this is expected to fail if the property does not exist, or __get() is not implemented
            // it is not reliably possible to check whether a property is accessible beforehand
            return $array->$key;
        }

        if (is_array($array)) {
            return (isset($array[$key]) || array_key_exists($key, $array)) ? $array[$key] : $default;
        }

        return $default;
    }
    /**
     * @param string $path
     *
     * @return bool
     */
    public function checkPublicList($path)
    {
        $publicList = self::getValue($this->getConfig(), 'public', []);
        return $this->checkPath($path, $publicList);
    }
    public function getConfig(){
        return self::getValue($this->getAuthConfig(), 'api', []);
    }
    /**
     * @param $path
     *
     * @return bool
     */
    public function checkWhitelist($path)
    {
        $whitelist = ArrayHelper::getValue($this->getConfig(), 'whitelist', []);
        return $this->checkPath($path, $whitelist);
    }
    /**
     * check url path
     *
     * @param string $path
     * @param array  $patterns
     *
     * @return bool
     */
    public function checkPath($path, array $patterns = []): bool
    {
        $status = false;
        foreach ($patterns as $pattern) {
            if (self::matchWildcard($pattern, $path)) {
                $status = true;
                break;
            }
        }
        return $status;
    }
    /**
     * Checks if the passed string would match the given shell wildcard pattern.
     * This function emulates [[fnmatch()]], which may be unavailable at certain environment, using PCRE.
     * @param string $pattern the shell wildcard pattern.
     * @param string $string the tested string.
     * @param array $options options for matching. Valid options are:
     *
     * - caseSensitive: bool, whether pattern should be case sensitive. Defaults to `true`.
     * - escape: bool, whether backslash escaping is enabled. Defaults to `true`.
     * - filePath: bool, whether slashes in string only matches slashes in the given pattern. Defaults to `false`.
     *
     * @return bool whether the string matches pattern or not.
     */
    public static function matchWildcard(string $pattern, string $string, array $options = []): bool
    {
        if ($pattern === '*' && empty($options['filePath'])) {
            return true;
        }

        $replacements = [
            '\\\\\\\\' => '\\\\',
            '\\\\\\*' => '[*]',
            '\\\\\\?' => '[?]',
            '\*' => '.*',
            '\?' => '.',
            '\[\!' => '[^',
            '\[' => '[',
            '\]' => ']',
            '\-' => '-',
        ];

        if (isset($options['escape']) && !$options['escape']) {
            unset($replacements['\\\\\\\\'], $replacements['\\\\\\*'], $replacements['\\\\\\?']);
        }

        if (!empty($options['filePath'])) {
            $replacements['\*'] = '[^/\\\\]*';
            $replacements['\?'] = '[^/\\\\]';
        }

        $pattern = strtr(preg_quote($pattern, '#'), $replacements);
        $pattern = '#^' . $pattern . '$#us';

        if (isset($options['caseSensitive']) && !$options['caseSensitive']) {
            $pattern .= 'i';
        }

        return preg_match($pattern, $string) === 1;
    }

}